﻿#region

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Radovici.SharedLibrary;
using YouTubePlaylist.MusicVideoService;

#endregion

namespace YouTubePlaylist.Models
{
    public class PlaylistModel : Notifiable, ISelector<VideoTrack>
    {
        #region Private fields

        private readonly DataLayer _model;
        private readonly List<VideoTrack> _notPlayedYet = new List<VideoTrack>();
        private readonly VideoPlaylist _playlist;
        private VideoTrack _curTrack;
        private VideoTrack _nextTrack;
        private VideoTrack _prevTrack;
        private string _selectedOption;
        private VideoTrack _selectedTrack;
        private bool _shuffleEnabled;

        #endregion

        public PlaylistModel(VideoPlaylist playlist)
        {
            _model = DataLayer.Singleton;
            if (playlist == null) throw new ArgumentException("Invalid Playlist; playlist can not be null.");
            _playlist = playlist;
            if (_playlist.Tracks == null)
                throw new ArgumentException("Invalid Playlist; playlist tracks can not be null.");
            _playlist.Tracks.CollectionChanged += OnTracksChanged;
            InitializeTracksManagement();
        }

        public DataLayer Model
        {
            get { return _model; }
        }

        public string SelectedOptionHelp
        {
            get
            {
                string help = null;
                switch (_selectedOption)
                {
                    case "mail":
                        help = "Mail this track to a friend.";
                        break;
                    case "link":
                        help = "Copy this track's link to your clipboard.";
                        break;
                    case "delete":
                        help = "Delete this track from this playlist.";
                        break;
                    case "refresh":
                        help =
                            "Refresh this track's videos (i.e., if you can't find what you're looking for in the current video list).";
                        break;
                }
                return help;
            }
        }

        public string SelectedOption
        {
            get { return _selectedOption; }
            set
            {
                _selectedOption = value;
                Notify("SelectedOption");
                Notify("SelectedOptionHelp");
            }
        }

        public VideoTrack SelectedTrack
        {
            get { return _selectedTrack; }
            set
            {
                _selectedTrack = value;
                Notify("SelectedTrack");
                Notify("IsTrackSelected");
            }
        }

        public bool IsTrackSelected
        {
            get { return _selectedTrack != null; }
        }

        public VideoPlaylist Playlist
        {
            get { return _playlist; }
        }

        public bool CanAddTracks
        {
            get { return _playlist.Id != 0; }
        }

        public bool HasTracks
        {
            get { return _playlist.Tracks != null && _playlist.Tracks.Count > 0; }
        }

        public bool ShuffleEnabled
        {
            get { return _shuffleEnabled; }
            set
            {
                _shuffleEnabled = value;
                InitializeTracksManagement();
            }
        }

        public bool IsPlaylistFinished
        {
            get { return _curTrack == null; }
        }

        #region ISelector<VideoTrack> Members

        public VideoTrack Current
        {
            get { return _curTrack; }
            set
            {
                if (_curTrack != null)
                    //TODO: Improve this, should happen automagically when updating preferences (property changed or something)
                {
                    _model.User.Preferences["CurrentPlaylist"] = _playlist.Id.ToString();
                    _model.User.Preferences["CurrentTrack"] = _curTrack.Id.ToString();
                    _model.UpdatePreferences();
                }
                _prevTrack = _curTrack;
                _curTrack = value;
                _nextTrack = GetNextTrack();
                NotifyAll();
            }
        }

        public bool HasPrevious
        {
            get { return Previous != null; }
        }

        public VideoTrack Previous
        {
            get { return _prevTrack; }
        }

        public bool HasNext
        {
            get { return Next != null; }
        }

        public VideoTrack Next
        {
            get { return _nextTrack; }
        }

        #endregion

        #region Private methods

        private VideoTrack GetNextTrack()
        {
            VideoTrack next = null;
            lock (_notPlayedYet)
            {
                if (_notPlayedYet.Count > 0)
                {
                    if (ShuffleEnabled)
                    {
                        int index = new Random().Next(_notPlayedYet.Count);
                        next = _notPlayedYet[index];
                    }
                    else
                    {
                        int curIndex = _playlist.Tracks.IndexOf(_curTrack);
                        next = _playlist.Tracks[(curIndex + 1)%_playlist.Tracks.Count];
                    }
                    _notPlayedYet.Remove(next);
                }
                else
                {
                    InitializeTracksManagement();
                }
            }
            return next;
        }

        private void InitializeTracksManagement()
        {
            lock (_notPlayedYet)
            {
                _notPlayedYet.Clear();
                _playlist.Tracks.ToList().ForEach(t => _notPlayedYet.Add(t));
            }
            if (Current == null && Playlist.Tracks.Count > 0)
            {
                Current = Playlist.Tracks[0];
            }
        }

        private void OnTracksChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            InitializeTracksManagement();
            NotifyAll(); //TODO: Throttle.
        }

        public void NotifyAll()
            //TODO: Make Notifiable function making use of reflection (public properties, possibly attributed).
        {
            Notify("SelectedOption");
            Notify("SelectedOptionHelp");
            Notify("SelectedTrack");
            Notify("IsTrackSelected");

            Notify("CanAddTracks");
            Notify("Playlist");
            Notify("ShuffleEnabled");
            Notify("HasTracks");
            Notify("Current");
            Notify("HasPrevious");
            Notify("Previous");
            Notify("HasNext");
            Notify("Next");

            Notify("IsPlaying");
            Notify("IsPlayerVisible");
        }

        #endregion

        public void Set(Track track)
        {
            Add(track);
        }

        public void Add(Track track)
        {
            var vt = new VideoTrack();
            vt.Artist = track.Artist;
            vt.Name = track.Name;
            SaveTrack(vt);
        }

        public void Set(VideoTrack track)
        {
            Current = track;
        }

        public void Add(VideoTrack track)
        {
            SaveTrack(track);
        }

        public void SaveTrack(VideoTrack track)
        {
            _model.AddTrack(_playlist, track);
            InitializeTracksManagement();
        }

        public void DeleteTrack(VideoTrack track)
        {
            _model.DeleteTrack(_playlist, track);
            InitializeTracksManagement();
        }

        public void RefreshTrack(VideoTrack track)
        {
            _model.RefreshTrack(track);
        }

        public void SaveTrackUrl(VideoTrack track)
        {
            _model.SaveTrackUrl(track);
            Notify("SelectedTrack");
            Notify("Current");
        }
    }
}